#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include "HttpProtocol.hpp"
#include "Socket.hpp"
#include "Log.hpp"

// http协议底层采用的是tcp协议
using func_t = std::function<std::string(std::string &)>;

class TcpServer; // 提前声明

class ThreadData
{
public:
    ThreadData(TcpServer *pthis, Socket *socket)
        : _pthis(pthis), _socket(socket)
    {
    }

    TcpServer *GetThis() { return _pthis; }
    Socket *GetSocket() { return _socket; }

    ~ThreadData()
    {
    }

private:
    TcpServer *_pthis;
    Socket *_socket;
};

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t handler_request)
        : _port(port), _handler_request(handler_request), _listensocket(new TcpSocket())
    {
        _listensocket->BuildListenSocketMethod(_port, backlog);
    }

    // 网络只负责IO发送
    static void *ThreadRun(void *args)
    {
        // 线程分离，主线程不需要再join了
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);

        std::string http_request;
        while (true)
        {
            // 1. 读取数据 -- 不关心数据是什么，只读取
            if(!td->GetSocket()->Recv(&http_request, 1024)) 
                break;
            std::string http_response = td->GetThis()->_handler_request(http_request);
            // 2. 发送数据 -- 不关心数据是什么，只发送
            if(!http_response.empty())
                td->GetSocket()->Send(http_response);
            else break;
        }

        // 注意这些资源释放的顺序
        td->GetSocket()->CloseSocket(); // 这个套接字不再用了，所以先close掉对应的fd
        delete td->GetSocket();         // 这个套接字类也是我们new出来的，需要手动释放
        delete td;                      // td也是我们new出来的，需要手动释放

        return nullptr;
    }

    void Loop()
    {
        while (true)
        {
            std::string peerip;
            uint16_t peerport;
            lg.LogMessage(Debug, "ready to accept, listensocketfd: %d\n", _listensocket->GetSockFd());
            Socket *newsock = _listensocket->AcceptConnection(&peerip, &peerport);
            if (newsock == nullptr)
                continue;
            lg.LogMessage(Info, "get a new link... sockfd: %d, client info: %s:%d\n", newsock->GetSockFd(), peerip.c_str(), peerport);

            pthread_t tid;
            ThreadData *td = new ThreadData(this, newsock);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    ~TcpServer()
    {
        delete _listensocket;
    }

private:
    uint16_t _port;        // 端口
    Socket *_listensocket; // listen套接字

public:
    func_t _handler_request;
};
